package com.magold.game.level;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.andengine.audio.music.Music;
import org.andengine.audio.music.MusicManager;
import org.andengine.audio.sound.Sound;
import org.andengine.audio.sound.SoundManager;
import org.andengine.engine.camera.SmoothCamera;
import org.andengine.engine.camera.hud.HUD;
import org.andengine.entity.IEntity;
import org.andengine.entity.scene.Scene;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.opengl.font.FontManager;
import org.andengine.opengl.shader.ShaderProgramManager;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.region.ITiledTextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

import android.content.res.AssetManager;
import android.hardware.SensorManager;

import com.badlogic.gdx.math.Vector2;
import com.magold.circuit.Circuit;
import com.magold.circuit.module.CircuitModule;
import com.magold.context.BaseMagoldActivity;
import com.magold.context.IContext;
import com.magold.context.IFixedEngine;
import com.magold.event.EventsProcessor;
import com.magold.game.MagoldCameraAdjuster;
import com.magold.game.MainGameActivity;
import com.magold.game.hud.GameStatusScene;
import com.magold.level.EntityBuilder;
import com.magold.level.ILevel;
import com.magold.logic.magold.AdditionalVelocityGainer;
import com.magold.logic.magold.Magold;
import com.magold.physics.MagoldContactListener;
import com.magold.skill.cast.AttacherManager;
import com.magold.skill.cast.AttacherRenderer;
import com.magold.sound.ISoundNotifier;
import com.magold.texture.TextureFactory;
import com.magold.touch.IHoldDetector;
import com.magold.touch.IPinchZoomDetector;
import com.magold.touch.ISceneEventDirector;
import com.magold.touch.ISwipeDetector;
import com.magold.touch.MagoldDirector;
import com.magold.touch.MagoldHoldDetector;
import com.magold.touch.MagoldPinchZoomDetector;
import com.magold.touch.MagoldSwipDetector;

public class MagoldLevel implements ILevel {
	public static final String TOTAL_COIN_KEY = "total_coin";
	public static final String GRAVITY_KEY = "gravity";
	public static final String BGM_KEY = "bgm";
	public static final String EVENT_PROCESSER_KEY = "event_processer";
	public static final String CAMERA_KEY = "camera";
	public static final String MAGOLD_KEY = "magold";
	private static final Vector2 DEFAULT_GRAVITY = new Vector2(0, SensorManager.GRAVITY_EARTH / 2);

	private final IContext mContext;
	private final Scene mScene;
	private final PhysicsWorld mPhysicsWorld;
	private final HashMap<Integer, Object> mIdentifiedMap;
	private final HashMap<String, Object> mAttributes;
	private List<EntityBuilder> mBuilders;

	public MagoldLevel(IContext pContext) {
		mContext = pContext;
		mScene = new Scene();
		mBuilders = new LinkedList<EntityBuilder>();
		mIdentifiedMap = new HashMap<Integer, Object>();

		mAttributes = new HashMap<String, Object>();
		mAttributes.put(GRAVITY_KEY, DEFAULT_GRAVITY);
		mAttributes.put(TOTAL_COIN_KEY, 0);
		mAttributes.put(EVENT_PROCESSER_KEY, new EventsProcessor());

		mPhysicsWorld = new PhysicsWorld(DEFAULT_GRAVITY, true);
	}

	@Override
	public Scene getScene() {
		return mScene;
	}

	@Override
	public Object setAttributes(String pKey, Object pValue) {
		return mAttributes.put(pKey, pValue);
	}

	@Override
	public Object getAttributeValue(String pKey) {
		return mAttributes.get(pKey);
	}

	@Override
	public PhysicsWorld getPhysicsWorld() {
		return mPhysicsWorld;
	}

	@Override
	public void onStart(BaseMagoldActivity pContext) {
		// prepare audio service.
		Music music = (Music) mAttributes.get(BGM_KEY);
		if (music != null) music.play();

		EventsProcessor processor = (EventsProcessor) mAttributes.get(EVENT_PROCESSER_KEY);
		mScene.registerUpdateHandler(processor);

		Vector2 cameraCoord = (Vector2) mAttributes.get(CAMERA_KEY);
		SmoothCamera camera = (SmoothCamera) getEngine().getCamera();
		camera.setXMin(cameraCoord.x);
		camera.setYMin(cameraCoord.y);

		Magold alpha = (Magold) getAttributeValue(MAGOLD_KEY);

		// prepare physics world.
		Vector2 gravity = (Vector2) mAttributes.get(GRAVITY_KEY);
		mPhysicsWorld.setGravity(gravity);
		MagoldContactListener contactSolver = new MagoldContactListener();
		mPhysicsWorld.setContactListener(contactSolver);
		mScene.registerUpdateHandler(contactSolver);
		mScene.registerUpdateHandler(mPhysicsWorld);
		
		AttacherManager am = getAttacherManager();
		am.addAttachListener(new AdditionalVelocityGainer());
		ITiledTextureRegion pRangeTexture = TextureFactory.createRepeatingImage("sprite/attacher_range.png");
		ITiledTextureRegion pAttacherTexture = TextureFactory.createImage("sprite/attacher.png");
		am.addAttachListener(new AttacherRenderer(pRangeTexture, pAttacherTexture, getVertexBufferObjectManager()));
		mScene.registerUpdateHandler(am);
		
		// prepare ui.
		HUD hud = new GameStatusScene(getVertexBufferObjectManager(), alpha, mScene, camera, (MainGameActivity) pContext);
		camera.setHUD(hud);

		// prepare user interactive.
		ISceneEventDirector director = new MagoldDirector();
		mScene.setOnSceneTouchListener(director);

		IPinchZoomDetector pinchZoom = new MagoldPinchZoomDetector(alpha.enableSprintSkill());
		director.registerDirectable(pinchZoom);

		ISwipeDetector swipe = new MagoldSwipDetector(alpha.enableCastMagnetSkill(this, mPhysicsWorld));
		director.registerDirectable(swipe);

		IHoldDetector hold = new MagoldHoldDetector(200, 100, alpha.enableSuppressSkill());
		director.registerDirectable(hold);
		
		camera.registerUpdateHandler(new MagoldCameraAdjuster(alpha.getSprite(), camera));
		
		float cw = (Float) mAttributes.get("camera_width");
		float ch = (Float) mAttributes.get("camera_height");
		camera.setBounds(0, 0, cw, ch);
		camera.setBoundsEnabled(true);
		
		// support sound.
		mScene.registerUpdateHandler(getSoundNotifier());
	}

	@Override
	public void putIdentifiedObject(int pID, Object pObject) {
		mIdentifiedMap.put(pID, pObject);
	}

	@Override
	public Object getIdentifiedObject(int pID) {
		return mIdentifiedMap.get(pID);
	}

	private final EventParamProvider mEventParamProvider = new EventParamProvider() {
		@Override
		public IEntity findEntity(int pID) {
			IEntity e = (IEntity) getIdentifiedObject(pID);
			if (e == null) {
				e = new EntityProxy<Integer>(pID, mIdentifiedMap);
			}

			return e;
		}

		@Override
		public Circuit findCircuit(int pID) {
			return (Circuit) getIdentifiedObject(pID);
		}
	};
	@Override
	public EventParamProvider getEventParamProvider() {
		return mEventParamProvider;
	}

	private final CircuitParamProvider mCircuitParamProvider = new CircuitParamProvider() {
		@Override
		public CircuitModule findCircuitModule(int pID) {
			return new CircuitModuleProxy(pID, mIdentifiedMap);
		}
	};
	@Override
	public CircuitParamProvider getCircuitParamProvider() {
		return mCircuitParamProvider;
	}

	@Override
	public VertexBufferObjectManager getVertexBufferObjectManager() {
		return mContext.getVertexBufferObjectManager();
	}

	@Override
	public AssetManager getAssets() {
		return mContext.getAssets();
	}

	@Override
	public IFixedEngine getEngine() {
		return mContext.getEngine();
	}

	@Override
	public FontManager getFontManager() {
		return mContext.getFontManager();
	}

	@Override
	public TextureManager getTextureManager() {
		return mContext.getTextureManager();
	}

	@Override
	public ShaderProgramManager getShaderProgramManager() {
		return mContext.getShaderProgramManager();
	}

	@Override
	public SoundManager getSoundManager() {
		return mContext.getSoundManager();
	}

	@Override
	public MusicManager getMusicManager() {
		return mContext.getMusicManager();
	}

	@Override
	public void setAttacherManager(AttacherManager pAttacherManager) {
		mContext.setAttacherManager(pAttacherManager);
	}

	@Override
	public AttacherManager getAttacherManager() {
		return mContext.getAttacherManager();
	}

	@Override
	public ISoundNotifier getSoundNotifier() {
		return mContext.getSoundNotifier();
	}

	@Override
	public void setSoundNotifier(ISoundNotifier pNotifier) {
		mContext.setSoundNotifier(pNotifier);
	}

	@Override
	public Sound getSoundByName(String pPath) {
		return mContext.getSoundByName(pPath);
	}

	@Override
	public void postBuilder(EntityBuilder pBuilder) {
		if (pBuilder != null) {
			mBuilders.add(pBuilder);
		}
	}

	@Override
	public void onBeforeLoad() {
	}

	@Override
	public void onAfterLoad() {
		for (EntityBuilder builder : mBuilders) {
			builder.build(mScene, mPhysicsWorld, getVertexBufferObjectManager());
		}
		mBuilders.clear();
	}


}
